package com.simplesoftwares.client.library.composite;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Vector;
import com.google.gwt.core.client.GWT;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.InlineLabel;
import com.simplesoftwares.client.library.appstructure.SuperModel;
import com.simplesoftwares.client.library.appstructure.SuperTable;
import com.simplesoftwares.client.library.appstructure.search.MyQuerry;
import com.simplesoftwares.client.library.libservice.OneToManyCompositeService;
import com.simplesoftwares.client.library.libservice.OneToManyCompositeServiceAsync;
import com.simplesoftwares.client.library.mywidgets.ObjectListBox;


/**
 * The Class OneToManyComposite.Component for object relation  ONE-MANY-CALCULABLE FROM MANY 
 * The class can handle following object relationship :->"One to Many" to Many+something or any thing which is 
 * calculatable from many.
 * Use case functionality :on opening of screen One ComboBox get filled from datastore.Many Combobox is empty.
 * Selecting any One Combobox item fills the many Combo with items related to one item.
 * On click of add button as per the logic provided by user the third entity table fills.
 * @param <T> the generic type Represents One type
 * @param <C> the generic type Represents Many type
 * @param <K> the key type Represents CALCULABLE FROM MANY  TYPE.
 */
public class OneToManyComposite<T extends SuperModel,C extends SuperModel,K >
extends Composite implements ChangeHandler,HasValue<List<? extends K>>
,Editor<List<? extends K>>,CompositeInterface,HasClickHandlers
{
	
	/** The one combo box. */
	private ObjectListBox<T> oneComboBox;
	
	/** The many combo box. */
	private ObjectListBox<C> manyComboBox;
	
	
	/** The one query. Decides entities in one combobox */
	private MyQuerry oneQuerry;
	
	/** The many query. Decides entities in many combobox*/
	private MyQuerry manyQuerry;
	
	
	
	/** The add button. */
	@Ignore
	private Button addButton;
	
	/** The content. */
	private FlowPanel content;
	
    /** The one to many hash map. This hashmap encapsulates the one to many relation */
    private  HashMap<T,List<C>>oneToManyHashMap;
	
	/** The many table. The datagrid for displaying calculable From Many objects */
	private  SuperTable<K>calculableFromMany;
	
	
	
	/** The name corresponding one relation entity 
	 * value of string is used as one Combobox label */
	private String oneRelationName;
	/** The name corresponding many relation entity 
	 * value of string is used as many Combobox label */
	private String manyRelationName;
	
	/** The service.
	 * provides service to this composite.
	 * Necessary to initiate hash map from server.
	 *  */
	private final OneToManyCompositeServiceAsync<T, C> service=GWT.create(OneToManyCompositeService.class);
	
	
	
	/**
	 * Instantiates a new one to many composite.
	 *
	 * @param many the many
	 */
	public OneToManyComposite(SuperTable<K>many) 
	{
	    
		content=new FlowPanel();
	    initWidget(content);
	    this.calculableFromMany=many;
	    createGui();
	    many.connectToLocal();
	    applyStyle();
	   }


     


	/**
	 * Instantiates a new one to many composite.
	 *
	 * @param oneQuerry  {@link MyQuerry} object corresponds to one relation.
	 * @param manyQuerry  {@link MyQuerry} object corresponds to many relation.
	 * @param manyTable  table that holds calculable from many objects.
	 */
	public OneToManyComposite(MyQuerry oneQuerry, MyQuerry manyQuerry,
			SuperTable<K> manyTable) 
	{
		this(manyTable);
		this.oneQuerry = oneQuerry;
		this.manyQuerry = manyQuerry;
		this.calculableFromMany = manyTable;
	
		
	}

	/**
	 * Gets the one relation name.
	 *
	 * @return the one relation name
	 */
	public String getOneRelationName() {
		return oneRelationName;
	}


     /**
	 * Sets the one relation name.
	 *
	 * @param oneRelationName the new one relation name
	 */
	public void setOneRelationName(String oneRelationName) {
		this.oneRelationName = oneRelationName;
	}

/**
	 * Gets the many relation name.
	 *
	 * @return the many relation name
	 */
	public String getManyRelationName() {
		return manyRelationName;
	}





	/**
	 * Sets the many relation name.
	 *
	 * @param manyRelationName the new many relation name
	 */
	public void setManyRelationName(String manyRelationName) {
		this.manyRelationName = manyRelationName;
	}





	/**
	 * Creates the ui for this composite
	 */
	private void createGui()
	{
        Grid prodgrid = new Grid(2,3);
		
		InlineLabel category = new InlineLabel(oneRelationName);
		InlineLabel productlbl = new InlineLabel(manyRelationName);
		this.oneComboBox=new ObjectListBox<T>();
		manyComboBox= new ObjectListBox<C>();
		addButton= new Button("Add");
		
		prodgrid.setWidget(0,0,category);
		prodgrid.setWidget(1, 0,oneComboBox); 
		
		prodgrid.setWidget(0,1,productlbl);
		prodgrid.setWidget(1, 1,manyComboBox);
		
		prodgrid.setWidget(0,2,new InlineLabel(""));
		prodgrid.setWidget(1, 2,addButton);
		
		prodgrid.setCellPadding(3);
		prodgrid.setCellSpacing(3);
		content.add(prodgrid);
		content.add(calculableFromMany.content);
	    oneComboBox.addChangeHandler(this);
	}
	
//Kamla make this proper
	/**
	 * Initialize hash maps.Initalizes the one to many hashmaps from server
	 */
	public void initializeHashMaps()
	{
		
		service.LoadOneToManyHashMap(oneQuerry, manyQuerry,new AsyncCallback<HashMap<T,List<C>>>() {

			@Override
			public void onFailure(Throwable caught) {
			
				
			}

			@Override
			public void onSuccess(HashMap<T, List<C>> result) {
				oneToManyHashMap=result;
				setCategoryListBox();
				
			}
		});
    }
	
	/**
	 * The method fills the many type with selected one type
	 * 
	 */
	protected void reactOnChange()
	{
		if(oneComboBox.getSelectedIndex()==0)
		{
			// Set many combobox to zeroth index.
			manyComboBox.setSelectedIndex(0);
			
		}
		
		else{
		
		T prodCat=oneComboBox.getSelectedItem();
	    List<C>vec=this.oneToManyHashMap.get(prodCat);
	    Vector<C> vecprod=new Vector<C>();
	    vecprod.addAll(vec);
	    System.out.println(vecprod.size()+"-------------------------------------------------");
	    manyComboBox.setListItems(vecprod);
		}
	}
	


	/* (non-Javadoc)
	 * @see com.google.gwt.event.dom.client.ChangeHandler#onChange(com.google.gwt.event.dom.client.ChangeEvent)
	 */
	
	@Override
	public void onChange(ChangeEvent event) 
	{
		reactOnChange();
	 }

    /**
     * Sets the category list box.This method sets the one combobox from one types entity
     */
    private void setCategoryListBox()
    {
    	Iterator<Entry<T, List<C>>>  it = oneToManyHashMap.entrySet().iterator();
    	Vector<T>catvector=new Vector<T>();
        while (it.hasNext()) {
            Entry<T, List<C>> pairs = it.next();
            catvector.add(pairs.getKey());
            }
        this.oneComboBox.setListItems(catvector);
    }
/*
 * 
 */
    /* (non-Javadoc)
 * @see com.google.gwt.user.client.ui.HasValue#getValue()
 */
@Override
	public List<K> getValue() {
		List<K>list=this.calculableFromMany.getDataprovider().getList();
		return list;
	}






/**
 * Gets the adds the button.
 *
 * @return the adds the button
 */
public Button getAddButton() {
		return addButton;
	}

	/**
	 * Sets the adds the button.
	 *
	 * @param addButton the new adds the button
	 */
	public void setAddButton(Button addButton) {
		this.addButton = addButton;
	}

	/**
	 * Gets the content.
	 *
	 * @return the content
	 */
	public FlowPanel getContent() {
		return content;
	}

	/**
	 * Sets the content.
	 *
	 * @param content the new content
	 */
	public void setContent(FlowPanel content) {
		this.content = content;
	}

	/**
	 * Gets the one to many hash map.
	 *
	 * @return the one to many hash map
	 */
	public HashMap<T, List<C>> getOneToManyHashMap() {
		return oneToManyHashMap;
	}

	/**
	 * Sets the one to many hash map.
	 *
	 * @param oneToManyHashMap the one to many hash map
	 */
	public void setOneToManyHashMap(HashMap<T, List<C>> oneToManyHashMap) {
		this.oneToManyHashMap = oneToManyHashMap;
	}

	

	

/* (non-Javadoc)
 * @see com.simplesoftwares.client.library.composite.CompositeInterface#validate()
 */
@Override
public boolean validate()
{
	if(this.calculableFromMany.getDataprovider().getList()==null||(this.calculableFromMany.getDataprovider().getList().size()==0))
			return false;
	else
		return true;
}

/* (non-Javadoc)
 * @see com.simplesoftwares.client.library.composite.CompositeInterface#clear()
 */
@Override
public void clear()
{
	this.calculableFromMany.connectToLocal();
}

/* (non-Javadoc)
 * @see com.simplesoftwares.client.library.composite.CompositeInterface#setEnable(boolean)
 */

public void setEnable(boolean status)
{
	oneComboBox.setEnabled(status);
	manyComboBox.setEnabled(status);
	calculableFromMany.setEnable(status);
	this.addButton.setEnabled(status);
	
}

/**
 * Apply style.
 */
public void applyStyle()
{
	content.setWidth("98%");
}









/* (non-Javadoc)
 * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler)
 */
@Override
public HandlerRegistration addValueChangeHandler(
		ValueChangeHandler<List<? extends K>> handler) {
	
	return null;
}





/* (non-Javadoc)
 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object)
 */
@Override
public void setValue(List<? extends K> value) {
	      calculableFromMany.connectToLocal();
	      calculableFromMany.getDataprovider().getList().addAll(value);
	
}





/* (non-Javadoc)
 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object, boolean)
 */
@Override
public void setValue(List<? extends K> value, boolean fireEvents) {

	
}





/**
 * Gets the one combo box.
 *
 * @return the one combo box
 */
public ObjectListBox<T> getOneComboBox() {
	return oneComboBox;
}





/**
 * Sets the one combo box.
 *
 * @param oneComboBox the new one combo box
 */
public void setOneComboBox(ObjectListBox<T> oneComboBox) {
	this.oneComboBox = oneComboBox;
}





/**
 * Gets the many combo box.
 *
 * @return the many combo box
 */
public ObjectListBox<C> getManyComboBox() {
	return manyComboBox;
}





/**
 * Sets the many combo box.
 *
 * @param manyComboBox the new many combo box
 */
public void setManyComboBox(ObjectListBox<C> manyComboBox) {
	this.manyComboBox = manyComboBox;
}





/**
 * Gets the many.
 *
 * @return the many
 */
public SuperTable<K> getMany() {
	return calculableFromMany;
}





/**
 * Sets the many.
 *
 * @param many the new many
 */
public void setMany(SuperTable<K> many) {
	this.calculableFromMany = many;
}





/* (non-Javadoc)
 * @see com.google.gwt.event.dom.client.HasClickHandlers#addClickHandler(com.google.gwt.event.dom.client.ClickHandler)
 */
@Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
	
	return this.addButton.addClickHandler(handler);
}

/**
 * Gets the one querry.
 *
 * @return the one querry
 */
public MyQuerry getOneQuerry() {
	return oneQuerry;
}

/**
 * Sets the one querry.
 *
 * @param oneQuerry the new one querry
 */
public void setOneQuerry(MyQuerry oneQuerry) {
	this.oneQuerry = oneQuerry;
}

/**
 * Gets the many querry.
 * @return the many querry
 */
public MyQuerry getManyQuerry() {
	return manyQuerry;
}

/**
 * Sets the many querry.
 * @param manyQuerry the new many querry
 */
public void setManyQuerry(MyQuerry manyQuerry) {
	this.manyQuerry = manyQuerry;
}








}
